%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Module summary
%%
%% Main authors: Peter
%% Progress to usefulness: 
%%
%% Ets master process, redirects calls to
%% ets workers.
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%
-module(ets_backend).
-behaviour(gen_server).
-include("db.hrl").

% gen_server API
-export([start_link/0,start_link/1]).

% gen_server API
-export([init/1, handle_call/3,handle_cast/2,handle_info/2
        ,terminate/2, code_change/3]).


start_link() ->
    io:format("Using ets backend\n"),
    gen_server:start_link({local,?BACKEND},ets_backend,[],[]).

start_link(Args) ->
    io:format("Using ets backend\n"),
    gen_server:start_link({local,?BACKEND},ets_backend,Args,[]).

% Initial dictionary
% should return {ok, State}
init(State) ->
    % Create new ets table
    State2 = State#ets{table = ets:new(table, 
         [set, public, {write_concurrency,true}, {read_concurrency,true}])},
    % Create worker state
    W_State = create_worker_state(State2),
    % Start workers, currently 20. Use environment variables instead?
    Workers = [Pid || {_,Pid} <- [ets_worker_sup:start_worker(W_State) 
                                  || _ <- lists:seq(1,20)]],
    % Put workers in the worker queue
    Queue   = queue:from_list(Workers),
    {ok, State2#ets{queue = Queue}}.


% Forward queries to the first worker in queue
handle_call(Msg, From, State) ->
    {{_,Worker}, Queue} = queue:out(State#ets.queue),
    gen_server:cast(Worker, {From, Msg}),
    % Spawn a new worker, keep number of workers constan
    {_, W} = ets_worker_sup:start_worker(create_worker_state(State)),
    Queue2 = queue:in(W, Queue),
    {noreply, State#ets{queue = Queue2}}.


handle_cast(Msg, State) ->
    {{_,Worker}, Queue} = queue:out(State#ets.queue),
    gen_server:cast(Worker, Msg),
    % Spawn a new worker, keep number of workers constan
    {_, W} = ets_worker_sup:start_worker(create_worker_state(State)),
    Queue2 = queue:in(W, Queue),
    {noreply, State#ets{queue = Queue2}}.


terminate(_Reason,State) ->
    ets:delete(State#ets.table),
    io:format("Terminating ets backend~n").


handle_info(Info, State) ->
    io:format("Info message received: ~w~n",[Info]),
    {noreply, State}.

code_change(_OldVsn, _State, _Extra) ->
    undefined.


% State helper function
create_worker_state(State) -> 
    #ets_worker{verbose = State#ets.verbose,
                table = State#ets.table}.
